package com.gmail.dengtao.joe.commons.crypto.mac;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.gmail.dengtao.joe.commons.util.Base64Utils;
import com.gmail.dengtao.joe.commons.util.HexUtils;
import com.gmail.dengtao.joe.commons.util.StringUtils;

/**
 * HMAC-SHA512 encrypt.
 * @author <a href="mailto:joe.dengtao@gmail.com">DengTao</a>
 * @version 1.0
 * @since 1.0
 */
public class HmacSHA512 {
	
	/** Stream buffer length */
    private static final int STREAM_BUFFER_LENGTH = 1024;
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result in bytes
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static byte[] encrypt(byte[] data, byte[] key) throws InvalidKeyException {
		Mac mac = MacUtils.getMac(MacUtils.HMAC_SHA_512);
		mac.init(new SecretKeySpec(key, MacUtils.HMAC_SHA_512));
		return mac.doFinal(data);
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a base64 string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptBase64(byte[] data, byte[] key) throws InvalidKeyException {
		return Base64Utils.bytesToBase64(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHex(byte[] data, byte[] key) throws InvalidKeyException {
		return HexUtils.bytesToHex(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a upper case hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHEX(byte[] data, byte[] key) throws InvalidKeyException {
		return HexUtils.bytesToHEX(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data to caculate
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result in bytes
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static byte[] encrypt(InputStream data, byte[] key) throws IOException, InvalidKeyException {
		byte[] buffer = new byte[STREAM_BUFFER_LENGTH];
        int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);
        Mac mac = MacUtils.getMac(MacUtils.HMAC_SHA_512);
        mac.init(new SecretKeySpec(key, MacUtils.HMAC_SHA_512));
        while (read > -1) {
        	mac.update(buffer, 0, read);
            read = data.read(buffer, 0, STREAM_BUFFER_LENGTH);
        }
        return mac.doFinal();
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data to caculate
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a base64 string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptBase64(InputStream data, byte[] key) throws IOException, InvalidKeyException {
		return Base64Utils.bytesToBase64(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data to caculate
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHex(InputStream data, byte[] key) throws IOException, InvalidKeyException {
		return HexUtils.bytesToHex(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data to caculate
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a upper case hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHEX(InputStream data, byte[] key) throws IOException, InvalidKeyException {
		return HexUtils.bytesToHEX(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result in bytes
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static byte[] encrypt(String data, String key) throws InvalidKeyException {
		return encrypt(data.getBytes(), key.getBytes());
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a base64 string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptBase64(String data, String key) throws InvalidKeyException {
		return Base64Utils.bytesToBase64(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHex(String data, String key) throws InvalidKeyException {
		return HexUtils.bytesToHex(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @return the HMAC-SHA-512 result as a upper case hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHEX(String data, String key) throws InvalidKeyException {
		return HexUtils.bytesToHEX(encrypt(data, key));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @param charset The name of the requested charset, may be null.
	 * @return the HMAC-SHA-512 result in bytes
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static byte[] encrypt(String data, String key, String charset) throws InvalidKeyException {
		return encrypt(StringUtils.getBytes(data, charset), StringUtils.getBytes(key, charset));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @param charset The name of the requested charset, may be null.
	 * @return the HMAC-SHA-512 result as a base64 string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptBase64(String data, String key, String charset) throws InvalidKeyException {
		return Base64Utils.bytesToBase64(encrypt(data, key, charset));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @param charset The name of the requested charset, may be null.
	 * @return the HMAC-SHA-512 result as a hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHex(String data, String key, String charset) throws InvalidKeyException {
		return HexUtils.bytesToHex(encrypt(data, key, charset));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @param charset The name of the requested charset, may be null.
	 * @return the HMAC-SHA-512 result as a upper case hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHEX(String data, String key, String charset) throws InvalidKeyException {
		return HexUtils.bytesToHEX(encrypt(data, key, charset));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @param charset The name of the requested charset, may be null.
	 * @return the HMAC-SHA-512 result in bytes
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static byte[] encrypt(String data, String key, Charset charset) throws InvalidKeyException {
		return encrypt(StringUtils.getBytes(data, charset), StringUtils.getBytes(key, charset));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @param charset The name of the requested charset, may be null.
	 * @return the HMAC-SHA-512 result as a base64 string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptBase64(String data, String key, Charset charset) throws InvalidKeyException {
		return Base64Utils.bytesToBase64(encrypt(data, key, charset));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @param charset The name of the requested charset, may be null.
	 * @return the HMAC-SHA-512 result as a hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHex(String data, String key, Charset charset) throws InvalidKeyException {
		return HexUtils.bytesToHex(encrypt(data, key, charset));
	}
	
	/**
	 * Processes the given array of bytes and finishes the HMAC-SHA-512 operation with the specified key.
	 * @param data data in bytes 
	 * @param key  the key material of the secret key. The contents of the array are copied to protect against subsequent modification.
	 * @param charset The name of the requested charset, may be null.
	 * @return the HMAC-SHA-512 result as a upper case hex string
	 * @throws InvalidKeyException
	 * @since 1.0
	 */
	public static String encryptHEX(String data, String key, Charset charset) throws InvalidKeyException {
		return HexUtils.bytesToHEX(encrypt(data, key, charset));
	}

}
